/*
 * Copyright 2012 Marsrutai.lt
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package lt.marsrutai.mobile.android.service.impl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import lt.marsrutai.mobile.android.AppContext;
import lt.marsrutai.mobile.android.R;
import lt.marsrutai.mobile.android.exception.DataAccessException;
import lt.marsrutai.mobile.android.model.Transport;
import lt.marsrutai.mobile.android.model.enums.TransportType;
import lt.marsrutai.mobile.android.service.ShapeManipulationService;
import lt.marsrutai.mobile.android.service.TransportInfoResolver;
import lt.marsrutai.mobile.android.util.StringUtils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.Pair;

/**
 * <p>Default implementation</p>
 *
 * @author Justas
 *
 */
public class TransportInfoResolverImpl implements TransportInfoResolver {

    protected Context context;

    protected ShapeManipulationService shapeManipulationService;

    private Map<Pair<String, String>, TransportInfo> transportInfoCache = new HashMap<Pair<String, String>, TransportInfo>();

    public TransportInfoResolverImpl(Context context) {
        this.context = context;
    }

    @Override
    public Drawable resolveTransportIcon(String scope, String transportId) {
        TransportInfo transportInfo = getTransportInfo(scope, transportId);

        switch (transportInfo.getTransportType()) {
        case WALK:
            return context.getResources().getDrawable(R.drawable.ic_walk);
        case BUS:
        case NIGHT_BUS:
            return resolveTransportIcon(transportInfo, R.drawable.ic_bus_shape, R.drawable.ic_bus_gradient);
        case TROLLEYBUS:
            return resolveTransportIcon(transportInfo, R.drawable.ic_trolleybus_shape, R.drawable.ic_trolleybus_gradient);
        case FERRY:
            return resolveTransportIcon(transportInfo, R.drawable.ic_ferry_shape, R.drawable.ic_ferry_gradient);
        case TRAIN:
        case METRO:
            return resolveTransportIcon(transportInfo, R.drawable.ic_train_shape, R.drawable.ic_train_gradient);
        case MINI_BUS:
        case PRIVATE_BUS:
        default:
            return resolveTransportIcon(transportInfo, R.drawable.ic_minibus_shape, R.drawable.ic_minibus_gradient);
        }
    }

    private Drawable resolveTransportIcon(TransportInfo transportInfo, int shape, int overlay) {
        if (transportInfo.getIcon() == null) {
            Drawable icon = shapeManipulationService.colorShape(context, shape, transportInfo.getColor());
            Bitmap combinedShapes = shapeManipulationService.combineShapes(icon, context.getResources().getDrawable(overlay));
            icon = shapeManipulationService.bitmapToDrawable(context, combinedShapes);
            transportInfo.setIcon(icon);
        }
        return transportInfo.getIcon();
    }

    @Override
    public int resolveTransportColor(String scopeFolder, String transportId) {
        return getTransportInfo(scopeFolder, transportId).getColor();
    }

    @Override
    public String resolveTransportPluralName(String scopeFolder, String transportId) {
        return getTransportInfo(scopeFolder, transportId).getLocalizedNamePlural();
    }

    @Override
    public void addTransportInfo(TransportInfo transportInfo) {
        transportInfoCache.put(new Pair<String, String>(transportInfo.getScopeFolder(), transportInfo.getTransportId()), transportInfo);
    }

    @Override
    public TransportInfo getTransportInfo(String scopeFolder, String transportId) {
        TransportInfo transportInfo = null;
        
        if (transportId != null && scopeFolder != null) {
            transportInfo = transportInfoCache.get(new Pair<String, String>(scopeFolder, transportId));

            if (transportInfo == null) {
                cacheTransportInfo(scopeFolder);
                transportInfo = transportInfoCache.get(new Pair<String, String>(scopeFolder, transportId));
            }
        }
        
        return transportInfo != null ? transportInfo : createNullTransportInfo(scopeFolder, transportId);
    }

    @Override
    public Set<TransportInfo> getTransportInfo(String scopeFolder) {
        Set<TransportInfo> result = new HashSet<TransportInfo>();
        boolean scopeCached = false;
        
        for (Pair<String, String> pair : transportInfoCache.keySet()) {
            if (StringUtils.equals(pair.first, scopeFolder)) {
                scopeCached = true;
                break;
            }
        }
        
        if (!scopeCached) {
            cacheTransportInfo(scopeFolder);
        }
        
        for (Entry<Pair<String, String>, TransportInfo> entry : transportInfoCache.entrySet()) {
            if (StringUtils.equals(entry.getKey().first, scopeFolder)) {
                result.add(entry.getValue());
            }
        }
        
        return result;
    }

    /**
     * Creates a <code>null</code> ("defult") transport info for cases where API
     * does not return any transport information for a transport belonging to
     * <code>scopeFolder</code> scope with <code>transportId</code> id.
     * 
     * @param scopeFolder
     * @param transportId
     * @return
     */
    protected TransportInfo createNullTransportInfo(String scopeFolder, String transportId) {
        return new TransportInfo(scopeFolder, transportId, TransportType.BUS, context.getString(R.string.null_transport_name), Color.BLACK, 999);
    }

    public void setShapeManipulationService(ShapeManipulationService shapeManipulationService) {
        this.shapeManipulationService = shapeManipulationService;
    }

    /**
     * Stores transport related info of all transport types available in the
     * provided scope
     *
     * @param scopeFolder
     */
    private void cacheTransportInfo(String scopeFolder) {
        List<Transport> transports;
        try {
            transports = AppContext.getDataService().getTransports(scopeFolder);
        }
        catch (DataAccessException e) {
            return;
        }

        for (int i = 0; i < transports.size(); i++) {
            Transport transport = transports.get(i);
            AppContext.getTransportInfoResolver().addTransportInfo(
                    new TransportInfo(scopeFolder, transport.getId(), transport.getType(), transport.getLocalizedNamePlural(),
                            Color.parseColor("#" + transport.getColor()), i));
        }
    }
}
